package com.imooc.controller;

import com.imooc.pojo.Users;
import com.imooc.pojo.vo.UsersVO;
import com.imooc.service.UserService;
import com.imooc.utils.IMOOCJSONResult;
import com.imooc.utils.JsonUtils;
import com.imooc.utils.MD5Utils;
import com.imooc.utils.RedisOperator;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;

@Controller //我们的测试包含一个页面,所以不需要用到RestController
public class SSOController {

    @Autowired
    private UserService userService;

    @Autowired
    private RedisOperator redisOperator;

    public static final String REDIS_USER_TOKEN = "redis_user_token";
    public static final String REDIS_USER_TICKET = "redis_user_ticket";
    public static final String REDIS_TMP_TICKET = "redis_tmp_ticket";

    public static final String COOKIE_USER_TICKET = "cookie_user_ticket";

    @GetMapping("/login")
    public String login(String returnUrl, Model model, HttpServletRequest request, HttpServletResponse response) {
        //将数据绑定模型thymeleaf
        model.addAttribute("returnUrl", returnUrl);
        //TODO 后续完善校验是否登录过

        //1.获取ticket门票,如果cookie中能够获取到,证明用户登录过,然后签发一个一次性的临时票据
        String userTicket = redisOperator.get(COOKIE_USER_TICKET);
        boolean isVerified = verifyUserTicket(userTicket);
        if (isVerified) {
            String tmpTicket = createTmpTicket();
            return "redirect:" + returnUrl + "?tmpTicket=" + tmpTicket;
        }
        //2.用户第一次进入cas的统一登录页面//跳转到CAS的统一login页面
        return "login";
    }

    /**
     * 校验CAS全局用户门票
     *
     * @param userTicket
     * @return
     */
    private boolean verifyUserTicket(String userTicket) {
        //0.门票不为空userTicket
        if (StringUtils.isBlank(userTicket)) {
            return false;
        }
        //1.验证cas门票是否有效
        String userId = redisOperator.get(REDIS_USER_TICKET + ":" + userTicket);
        if (StringUtils.isBlank(userId)) {
            return false;
        }
        //2.验证是否在user会话
        String userRedis = redisOperator.get(REDIS_USER_TOKEN);
        if (StringUtils.isBlank(userRedis)) {
            return false;
        }
        return true;
    }

    /**
     * cas的统一登录接口
     * 1.登录后创建用户的全局会话  => uniqueToken
     * 2.创建用户的全局门票用以表示cas端是否登录 =>userTicket
     * 3.创建用户的临时票据,用于回调回传  =>tmpTicket
     *
     * @param username
     * @param password
     * @param returnUrl
     * @param model
     * @param request
     * @param response
     * @return
     * @throws Exception
     */
    @PostMapping("/doLogin")
    public String doLogin(String username, String password, String returnUrl, Model model,
                          HttpServletRequest request, HttpServletResponse response) throws Exception {
        model.addAttribute("returnUrl", returnUrl);

        //0.判断用户名和密码不为空
        if (StringUtils.isBlank(username) || StringUtils.isBlank(password)) {
            model.addAttribute("errmsg", "用户名或密码不能为空");
            return "login";
        }

        //1.实现登录
        Users userResult = userService.queryUserForLogin(username, MD5Utils.getMD5Str(password));

        if (userResult == null) {
            model.addAttribute("errmsg", "用户名或密码不正确");
            return "login";
        }

        //2.实现用户的redis会话
        String uniqueToken = UUID.randomUUID().toString().trim();
        UsersVO usersVO = new UsersVO();
        BeanUtils.copyProperties(userResult, usersVO);
        //设置token
        usersVO.setUserUniqueToken(uniqueToken);
        redisOperator.set(REDIS_USER_TOKEN + ":" + userResult.getId(), JsonUtils.objectToJson(usersVO));

        //3.生成ticket门票,全局门票
        String userTicket = UUID.randomUUID().toString().trim(); //使用trim可以保证末尾没有空格的存在,不加其实也可以,引入第三方依赖jar包时产出字符串有空格的话就会出bug

        //3.1 用户全局门票需要放入CAS端的cookie中
        // setCookie(COOKIE_USER_TICKET, userTicket, response);
        //TODO 这里出现cookie无法获得的现象,我先放到redis里
        redisOperator.set(COOKIE_USER_TICKET, userTicket);

        //4.userTicket也要关联用户id,放入redis,代表用户有门票可以在动物园的各个景区游玩
        redisOperator.set(REDIS_USER_TICKET + ":" + userTicket, userResult.getId());

        //5.生成临时票据,回调到调用端网站,由CAS签发的一个一次性的临时ticket
        String tmpTicket = createTmpTicket();

        // return "login";
        return "redirect:" + returnUrl + "?tmpTicket=" + tmpTicket;
    }

    @PostMapping("/verifyTmpTicket")
    @ResponseBody
    public IMOOCJSONResult verifyTmpTicket(String tmpTicket, HttpServletRequest request, HttpServletResponse response) throws Exception {

        //使用一次性临时票据验证用户是否登录过,如果登陆过,把用户的会话信息返回给各个站点,使用完毕后销毁临时票据
        String tmpTicketValue = redisOperator.get(REDIS_TMP_TICKET + ":" + tmpTicket);
        if (StringUtils.isBlank(tmpTicketValue)) {
            return IMOOCJSONResult.errorUserTicket("用户的票据异常");
        }

        //0.如果临时票件有效,需要销毁临时票据并且拿到CAS端cookie中的全局UserTicket,然后获取用户会话
        if (!tmpTicketValue.equals(MD5Utils.getMD5Str(tmpTicket))) {
            return IMOOCJSONResult.errorUserTicket("用户的票据异常");
        } else {
            //如果和md5加密后相同则销毁
            redisOperator.del(REDIS_TMP_TICKET + ":" + tmpTicket);
        }

        //1.验证并且获取用户的userTicket
        // String userTicket = this.getCookie(request, COOKIE_USER_TICKET);
        String userTicket = redisOperator.get(COOKIE_USER_TICKET);
        if (StringUtils.isBlank(userTicket)) {
            return IMOOCJSONResult.errorUserTicket("用户的票据异常");
        }
        //得到UserId
        String userId = redisOperator.get(REDIS_USER_TICKET + ":" + userTicket);
        if (StringUtils.isBlank(userId)) {
            return IMOOCJSONResult.errorUserTicket("用户的票据异常");
        }
        //通过userId获得会话
        //2.验证门票对应的user会话是否存在
        String userRedis = redisOperator.get(REDIS_USER_TOKEN + ":" + userId);
        if (StringUtils.isBlank(userRedis)) {
            return IMOOCJSONResult.errorUserTicket("用户的票据异常");
        }

        //3.验证成功后携带用户会话返回
        return IMOOCJSONResult.ok(JsonUtils.jsonToPojo(userRedis, UsersVO.class));
    }

    @PostMapping("/logout")
    @ResponseBody
    public IMOOCJSONResult logout(String userId, HttpServletRequest request, HttpServletResponse response) {
        //0.获取cas用户门票
        String userTicket = redisOperator.get(COOKIE_USER_TICKET);
        //1.清楚userTicket票据,redis/cookie
        redisOperator.del(REDIS_USER_TICKET + ":" + userId);
        redisOperator.del(REDIS_USER_TOKEN + ":" + userTicket);
        redisOperator.del(COOKIE_USER_TICKET + ":" + userTicket);
        return IMOOCJSONResult.ok("用户已退出登录");
    }

    /**
     * 创建临时票据
     *
     * @return
     */
    private String createTmpTicket() {
        String tmpTicket = UUID.randomUUID().toString().trim();
        try {
            redisOperator.set(REDIS_TMP_TICKET + ":" + tmpTicket, MD5Utils.getMD5Str(tmpTicket), 600);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return tmpTicket;
    }

    private void deleteCookie(String key, String val, HttpServletResponse response) {
        Cookie cookie = new Cookie(key, val);
        //设置cookie属性
        cookie.setDomain("sso.com");
        cookie.setPath("/");
        cookie.setMaxAge(-1);
        response.addCookie(cookie);
    }

    // private void setCookie(String key, String val, HttpServletResponse response) {
    //     Cookie cookie = new Cookie(key, val);
    //     //设置cookie属性
    //     cookie.setDomain("sso.com");
    //     cookie.setPath("/");
    //     response.addCookie(cookie);
    // }

    // private String getCookie(HttpServletRequest request, String key) {
    //     Cookie[] cookieList = request.getCookies();
    //     if (cookieList == null || StringUtils.isBlank(key)) {
    //         return null;
    //     }
    //     String cookieValue = null;
    //     for (int i = 0; i < cookieList.length; i++) {
    //         if (cookieList[i].getName().equals(key)) {
    //             cookieValue = cookieList[i].getValue();
    //             break;
    //         }
    //     }
    //     return cookieValue;
    // }
}